﻿using Circus.Utility;
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.SessionState;
using Circus.Web;

namespace H5.FIO {

    /// <summary>
    /// 负责处理地址命令并执行调用相应的方法。
    /// </summary>
    public class ApiHandler : IHttpHandler, IRequiresSessionState {

        private const string typePrefix = "H5.FIO.";

        private const string typePostfix = "Controller";

        /// <summary>
        /// 获取一个值，该值指示其他请求是否可以使用 <see cref="T:System.Web.IHttpHandler"/> 实例。
        /// </summary>
        /// <returns>
        /// 如果 <see cref="T:System.Web.IHttpHandler"/> 实例可再次使用，则为 true；否则为 false。
        /// </returns>
        public bool IsReusable {
            get {
                return true;
            }
        }

        /// <summary>
        /// 通过实现 <see cref="T:System.Web.IHttpHandler"/> 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
        /// </summary>
        /// <param name="context"><see cref="T:System.Web.HttpContext"/> 对象，它提供对用于为 HTTP 请求提供服务的内部服务器对象（如 Request、Response、Session 和 Server）的引用。</param>
        public void ProcessRequest(HttpContext context) {
            var path = context.Request.Path;
            int startIndex = path.Length;

            // 忽略最后一个 '/'
            if (path[startIndex - 1] == '/') {
                startIndex--;
            }

            // 检索 /commandName 的位置。
            var left = lastIndexOf(path, '/', startIndex - 1) + 1;
            string commandName = path.Substring(left, startIndex - left);

            // 跳过命令名。
            startIndex = left - 2;

            // 检索 /commandType/ 的位置。
            left = lastIndexOf(path, '/', startIndex - 1) + 1;
            string commandType = String.Concat(typePrefix, char.ToUpperInvariant(path[left]).ToString(), path.Substring(left + 1, startIndex - left), typePostfix);

            executeCommand(commandType, commandName, context.Request.Params);
        }

        private static int lastIndexOf(string value, char ch, int startIndex) {
            for (var i = startIndex; i >= 0; i--) {
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        }

        private static void reportError(string message) {
            HttpContext.Current.Response.StatusCode = 404;
            HttpContext.Current.Response.Write(message);
        }

        public static void executeCommand(string commandType, string commandName, NameValueCollection args) {
            var type = typeof(ApiHandler).Assembly.GetType(commandType);
            if (type == null) {
                reportError("Command Type Not Found: " + commandType);
                return;
            }
            var method = type.GetMethod(commandName);
            if (method == null) {
                reportError("Command Name Not Found: " + commandName);
                return;
            }
            var ps = method.GetParameters();
            var values = new object[ps.Length];
            for (int i = 0; i < ps.Length; i++) {
                var p = ps[i];
                var arg = args[p.Name];
                if (arg != null) {
                    values[i] = convertTo(arg, p.ParameterType);
                } else if (p.IsDefined(typeof(System.Runtime.InteropServices.OptionalAttribute), false)) {
                    values[i] = p.DefaultValue;
                } else {
                    //reportError("Missing Command Argument : " + p.Name);
                    //return;
                }
            }
            var result = method.Invoke(null, values);
            if (method.ReturnType != typeof(void)) {
                HttpHelper.jsonp(result);
            }
        }

        private static object convertTo(string arg, Type type) {
            if (type == typeof(string)) {
                return arg;
            }
            if (type == typeof(int)) {
                return ConvertHelper.parseInt(arg);
            }
            if (type == typeof(bool)) {
                return ConvertHelper.parseBool(arg);
            }
            if (type == typeof(double)) {
                return ConvertHelper.parseFloat(arg);
            }
            if (type == typeof(DateTime)) {
                return ConvertHelper.parseDateTime(arg);
            }
            return arg;
        }

    }

}